package com.jy.rhin.domain.document.filter.impl;

import com.jy.rhin.domain.document.filter.ContentEncoder;
import com.jy.rhin.domain.document.filter.DocumentFilter;
import com.jy.rhin.domain.document.filter.DocumentFilterDescEnum;
import com.jy.rhin.domain.exception.RhinInfraErrorType;
import com.jy.rhin.infra.exception.RhinInfraException;
import com.jy.rhin.infra.uitl.Asserts;
import com.jy.rhin.sdk.dto.document.DocumentContentDto;
import com.jy.rhin.sdk.dto.document.DocumentEntryDto;
import com.jy.rhin.sdk.dto.document.DocumentSubmissionSetDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author WeiQ
 * @date 2022/6/9 10:46
 * @Desc 文档内容验证：content能够base64解码，并计算hash/size与入参中的hash/size进行对比
 */
@Slf4j
@Service
public class DocumentFilter4ContentValid implements DocumentFilter {

    @Resource
    ContentEncoder contentEncoder;

    @Override
    public boolean shouldFilter(DocumentSubmissionSetDto submissionSet, List<DocumentContentDto> contentList) {
        return !CollectionUtils.isEmpty(contentList);
    }

    /**
     * 文档内容验证
     * 遍历校验所有的文档内容
     * 1、验证文档content内容是否能base64解码
     * 2、根据content内容计算size以及hash，从提交集的条目列表中获取文档对应的entry，对比entry下size/hash值，是否都相等
     *
     * @param submissionSet
     * @param contentList
     */
    @Override
    public void doFilter(DocumentSubmissionSetDto submissionSet, List<DocumentContentDto> contentList) {
        Map<String, DocumentEntryDto> entryDtoMap = submissionSet.getEntries().stream().collect(Collectors.toMap(DocumentEntryDto::getId, dto -> dto));
        for (DocumentContentDto documentContentDto : contentList) {
            String content = documentContentDto.getContent();
            try {
                //获取content内容byte数组
                byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
                //尝试进行base64解码，如果解码异常，则校验不通过，文档内容解析失败
                final Base64.Decoder decoder = Base64.getDecoder();
                byte[] base64Decode = decoder.decode(bytes);
                //content计算hash和size
                String hash = contentEncoder.sign(base64Decode);
                int size = base64Decode.length;
                //获取文档对应的entry
                DocumentEntryDto documentEntryDto = entryDtoMap.get(documentContentDto.getId());
                //对比hash/size
                boolean signResult = documentEntryDto.getSize() == size && StringUtils.equals(documentEntryDto.getHash(), hash);
                Asserts.isTrue(signResult, RhinInfraErrorType.CONTENT_SIZE_OR_HASH_ERROR);
                if (log.isDebugEnabled()) {
                    log.debug("文档内容hash/size校验, 期望:[hash={}, size={}], 实际接收:[hash={}, size={}]", hash, size, documentEntryDto.getHash(), documentEntryDto.getSize());
                }
            } catch (RhinInfraException e) {
                throw e;
            } catch (Exception e) {
                throw new RhinInfraException(RhinInfraErrorType.DOCUMENT_CONTENT_VALID_ERR);
            }

        }
    }

    @Override
    public DocumentFilterDescEnum filterDesc() {
        return DocumentFilterDescEnum.CONTENT_VALID;
    }
}
